LEDフリーラン点滅
タイマ割り込み(Dynamic Timer Drivers Library)を使った場合
(→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード)
LEDのフリーラン点滅を タイマ割り込み(Dynamic Timer Drivers Library)を使って行う場合の例を以下に説明します。 | |
<仕様> ・ PIC32MZを使用する ・ LEDは1sec ON、1sec OFFを繰り返す ・ 動作クロックは外部に24MHzの水晶発振器を接続しこれを使用する ・ 時間制御は、割り込みを使い Harmonyのダイナミックタイマドライバー ライブラリを使うこと。 |
|
<回路図>(→回路図のPDFファイル)
|
<外観>
<動作結果>
(画像をクリックすると プログラム動作中の動画がYouTubeで再生します)
ディスクリートキバン (PIC32MZ QFPモジュール組立キット → 購入方法) (QFPモジュール用ユニバーサルキバン → 購入方法) |
PIC32MZ評価ボード(MZ100-A001) ( → 購入方法 ) |
|
動作結果の 動画再生 |
![]() |
![]() |
■ 解説 |
右の表は、MHCでダイナミックタイマドライバーライブラリー(以下、ダイナミックタイマと云う)を選択した場合に生成されるファイルの抜粋です。 ソースコードの黒字の部分はMHCが自動生成する部分です。緑字部分がLEDフリーラン(LED 1秒点灯、1秒消灯の繰り返し)として追加記入した部分です。 ■ 基本的な使い方 ダイナミックタイマを使った場合の基本的方法は以下です。 (1) タイマハンドル取得 myHandle = DRV_TMR_Open ( ); (2) 周期レジスタ初期値セット uint32_t divider = .; (3) コールバック関数繰り返し呼び出しをセットアップ DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR ); (4) タイマ割り込み許可、タイマ1イネーブル DRV_TMR_Start(myHandle); コールバック関数名はアプリケーション側で(Harmonyの関数とバティングしない範囲で)自由に命名できます。 ■ 関数呼び出しフロー ダイナミックタイマに於ける関数呼び出しのフローは以下のようになっています。かなり複雑です。 (1) PICが起動するとmain.cのSYS_Initialize()からsystem_init.cのSYS_Initialize()が呼び出され初期化が始まります。 (2) 本例の場合はタイマ1への入力周波数(ペリフェラルクロック3) を10MHzにするために、PB3DIVレジスタの所要ビットをDIV3DIVbits.PBDIV = 0x13に変更します。 このレジスタ変更に際してはシステムロックが必要となります。 (3)関数DRV_TMR_Initialize(DRV_TMR_INDEX_0, (SYS_MODULE_INIT *)&drvTmr0InitData);でタイマ1が初期化されます。引数のDRV_TMR_INDEX_0(値:0)はドライバーのインデックス番号でMHCが自動的に割り当ててくれます。本例では(データシート上の)ハードウェアタイマ1がダイナミックタイマドライバーDRV_TMR_INDEX_0に割り当てられています。 (4)アドレスが引数となっているdrvTmr0InitDataはダイナミックタイマの諸元を規定している構造体DRV_TMR_INITのインスタンスで、system_init.cの中で初期化されています。 この初期化された値の内容が具体的に明示されているのはsystem_config.hの中です。 (5) SYS_Initialize()のDRV_TMR_Initialize( )はdrv_tmr.cのDRV_TMR_Initialize( )を呼び出します。 (6) drv_tmr.cのDRV_TMR_Initialize( )に於いて ダイナミックタイマの諸元の構造体DRV_TMR_INITのインスタンスtmrInitを生成して、この(ローカル)インスタンスに引数として取得したアドレスをコピーします。 (7)一方、タイマ用システムモジュールのディスクリプタのpTmrDcptインスタンスも生成し、ドライバーのインデックスが規定の範囲内にあるか、また既に初期化された同じインデックスがないかなどをチェックしています。 (8) また、タイマ用システムモジュールのインスタンスpTmrInstも生成し、それぞれの対応するメンバーにtmrInitのメンバーをコピーして初期化します。 (9) タイマ用システムモジュールのハンドルを戻り値として返します。 (この戻り値は タイマドライバーのハンドルではありません。タイマドライバーのハンドルはDRV_TMR_Open( )の戻り値です。) (10) タイマ用システムモジュールのハンドルを取得します。これでタイマドライバーのハンドルを取得する準備ができました。 (11)app.cの中にあるAPP_Initialize( )のDRV_TMR_Open( )でタイマドライバーを初期化します。 引数はタイマ1が引き当てられているDRV_TMR_INDEX_0 と ドライバーが(マルチクライアントでない)単独クライアント用であることを明示する DRV_IO_INTENT_EXCLUSIVE にします。
(12)tmr_drv.cのDRV_HANDLE DRV_TMR_Open( )を呼び出します。 (13)タイマ用システムモジュールが初期化確認などの所要事項の確認後、タイマードライバーのハンドルを戻り値として返します。 (14) タイマドライバーのハンドルmyHandleを取得します。 (15) DRV_TMR_AlarmRegister( )でコールバック関数の繰り返し呼び出しをセットアップします。 (16)DRV_TMR_AlarmRegiste( )を呼び出します。 この関数の第5引数 DRV_TMR_CALLBACK callBackは関数へのポインタで、drv_tmr.hの中で定義されています。 (17)DRV_TMR_Start( )でタイマの割り込みを許可し、イネーブル状態にします。 (18)タイマ割り込みが許可されるとsystem_interrupt.cの中にある割り込みベクトル関数__ISR(_TIMER_1_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0( )がタイマカウンタと周期レジスタ値が一致すると呼び出されます。 そしてDRV_TMR_Tasks( );が呼び出されます。 (19)drv_tmr.cのDRV_TMR_Tasks( )が呼び出されます。 (20)次にDRV_TMR_Tasks()の中にある DRV_TMR_ProcessEvents( )がよばれます。 (21)この関数の中で コールバック関数へのポインタDRV_TMR_CALLBACKなどををメンバーにもつDRV_TMR_CLIENT_OBJ構造体のポインタとしてpClientを宣言し、初期化します。 (22)コールバック関数の呼び出しが行われます。 (23) 以下、(18)〜(22)の繰り返しが行われます。 |
|
■ プロジェクト作成手順(ダイナミックタイマ割り込みを使った場合) はじめてHarmonyの プロジェクトをつくる方も対象に、プロジェクト作成要領について説明します。 以下は、Harmony v1.08、MPLABX v.3.35 をもとに作成してあります。 (10)のPin Settinsのポート設定の画面がHarmony v.2以降大幅に 変わりましたのでポート設定画面は v2.04のものに変更してあります。 Harmony v.2.04の時点では、他の部分の変更はありません。 (最新の Harmony や MPLABX とは、若干異なるところがある可能性がありますので注意してください。) |
||
(1) | Harmony プロジェクト選択 | |
MPLABXのメニューバーで[File]→[New Project]とすると 下図の[New Project]ダイアログが開きます。[Categories]の[Microchip Embedded]を選んだあと [Projects]の[32bit−MPLAB Harmony Project]を選択して[Next]をクリックします。 | ||
![]() |
||
(2) | プロジェクトフォルダの作成 | |
C:\microchip\harmony\v1_08_01\appsフォルダに下にプロジェクトフォルダをつくります。 これ以外のところにプロジェクトフォルダをつくるとコンパイルできなくなることがあります。 今回だけでなく後々他にもたくさんHarmonyのプロジェクトをつくことになるのでここではC:\microchip\harmony\v1_08\apps\_myWork\Test\Basicフォルダの下にプロジェクトフォルダをつくります。 | ||
![]() |
||
(3) | プロジェクト名、デバイス名 | |
ここではプロジェクト名を"03 Led FreeRun Dynamic tmrINT"として、Project Nameに記入します。使用するデバイス名としてPIC32MZ2048EFH100を選択して [Finish]をクリックします。 | ||
![]() |
||
(4) | プロジェクト画面の表示 | |
プロジェクト全体の画面があらわれます。 | ||
![]() |
||
(5) | プロジェクトのプロパティ設定 | |
プロジェクトを右クリックしてドロップダウンリストの中から[Properties]を選択します。 | ||
![]() |
||
(6) | コンパイラの設定 | |
Project Propertiesのダイアログが開きます。特に理由がなければXC32の最新のコンパイラを選択します。 | ||
![]() |
||
(7) | シフトJIS選択 | |
Categoriesボックスの[General]を選択して、 Encodiongのコンボボックスから[Shift-JIS]を選択します。 | ||
![]() |
||
(8) | MHCの表示 | |
右のボックスにMHC(MPLAB Harmony Configurator)の画面が表示されていない場合は、 メニューバーから[Tools] → [Embedded] → [MPLAB
Harmony Configurator]をクリックするとMHCの画面が現れます。 尚、MHC画面を表示するには左側のプロジェクトWinodowでMHCを開きたいプロジェクトをドロップダウンリストで main project に設定しておく必要があります。 |
||
![]() |
||
(9) | クロックの設定 | |
[Clock Diagram]のタブをクリックしてクロック設定の画面を開きます。 PIC32MZのOSC1ピンに 24MHzの外付け水晶発振器を取り付け、システムクロックが200MHzとなるクロックモジュールの設定を行います。プリプロセッサコマンドで記述すると以下のようになります。 #pragma config FNOSC = SPLL #pragma config POSCMOD = EC #pragma config FPLLICLK = PLL_POSC #pragma config FPLLIDIV = DIV_3 #pragma config FPLLMULT = MUL_50 #pragma config FPLLODIV = DIV_2 この設定を実現すべくクロックモジュール設定画面で FNOSC、 POSCMOD、 FPLLICLK、 FPLLIDIV、FPLLMULT、FPLLODIVの各項目のコンボボックスからそれぞれ SPLL、 EC、 PLL_POSC、 DIV_3、 MUL_50、 DIV_2を選択します。 (下記画面は 画像をクリックすると画像が拡大します) |
||
![]() |
||
<追記> クロックの設定は以下の MHCのDevice & Project Configuration画面でも設定できます。 ![]() |
||
(10) | ポートの設定 | |
ポートの設定(下記のPin Settingsの画面は Harmony v2.04のものです) [Pin Settings]のタブを開きます。 Pinの RG15で Function欄を GPIO_OUT に変更します ![]() |
||
(11) | ダイナミックタイマ選択(Dynamic Timer Drivers Librayを選択 ) | |
MHCの[Option]タブを選択して、ツリーからHarmony Framework Configuration→Driver→Timerを選択します。そして"Use
Timer Driver ?"のチェックに入れ、Driver Implementationで "DYNAMIC"を選択します。 更に、"Interrupt
Mode"、"TMR Driver Instance 0" のチェックをいれます。後は、デフォルト(タイマ1選択、割り込み優先度1、内部タイマクロック、プリスケーラ1/256など)のままにしておきます。 |
||
![]() |
||
(12) | プロジェクト コードの生成 | |
![]() |
||
![]() Generateボタンをクリックしてプロジェクトファイルを一式生成します。 ![]() |
||
(13) | 変数の宣言 | |
プロジェクトファイルが一式できあがりました。クリーンビルドしてここまで問題なくプロジェクトファイルができあがっていることを確認します。 アプリケーションのコーディングの最初として、app.cの中にタイマ1のハンドルとLEDの状態フラグの変数を宣言します。 DRV_HANDLE myHandle; bool LED; |
||
![]() |
||
(14) | コールバック関数の定義 | |
次に コールバック関数を定義します。 コールバック関数には、呼ばれた場合次の処理をする実行分を書き込みます。 ・LEDが消灯していたら点灯し、点灯時間を周期レジスタに書き込む ・LEDが点灯していたら消灯し、消灯時間を周期レジスタに書き込む
![]() |
||
(15) | ハンドル取得、割り込み許可 | |
app.cの中の APP_Initialize( )関数の中で @タイマ1のドライバーをオープンして、戻り値としてハンドルを取得します。 A最初のタイマ1の割り込み発生までの時間を周期レジスタに設定します。 B繰り返しコールバック関数を呼び出すようにセットアップします。 Cタイマ1の割り込みを許可し、タイマ1をイネーブルにします。 void APP_Initialize ( void ) { .... .... myHandle = DRV_TMR_Open ( DRV_TMR_INDEX_0, DRV_IO_INTENT_EXCLUSIVE ); //タイマ1ハンドル取得 uint32_t divider = 39063; //周期レジスタ初期値 //最初の割り込みまでの時間をセット //5 nsec x20 x 39063 x 256 = 1000.0128msec = 1sec at Fblclk3 = 10MHz DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR ); //繰り返し呼び出し //コールバック関数繰り返し呼び出しをセットアップ // bool DRV_TMR_AlarmRegister( // DRV_HANDLE handle, // uint32_t divider, //周期レジスタ初期値 // bool isPeriodic, //周期性の有無 // uintptr_t context, // DRV_TMR_CALLBACK callBack // ); DRV_TMR_Start(myHandle); //タイマ1割り込み許可、タイマ1イネーブル } |
||
(16) | タイマ入力周波数変更 | |
デフォルトでは、タイマの入力クロックは100MHzです。従ってプリスケール1/256の16ビットタイマの最大カウント時間は 167.7696 msec( = 10nsec x 256 x 65535 = 167769600 nsec)です。 所要の1secより短いのでタイマの入力クロックの ペリフェラルクロック(PBCLK3)を10MHzに変更します。PB3DIVレジスタを変更することによってペリフェラルクロック (PBCLK3)の周波数を変えることができます。 PBCLK3レジスタを変更するには、事前にシステムロックを実施し、 変更後にシステムロックを解除する必要があります SYS_DEVCON_SystemUnlock(); PB3DIVbits.PBDIV = 0x13; SYS_DEVCON_SystemLock(); ![]() |
||
(17) | プロジェクトのクリーンビルド | |
プロジェクトのクリーンとビルドを行います。 | ||
![]() |
||
実行ファイルをデバイスに書き込みます。 |
||
![]() |